package controller.commoncontroller;

import view.commonview.ButtonValues;
import view.commonview.MenuBar;
import view.commonview.ToolBar;

import java.util.Observable;
import java.util.Observer;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import view.commonview.TabbedPane;
import model.editormodel.EditorModel;
import model.editormodel.ErrorMessage;
import model.editormodel.ErrorMessage.ErrorType;
import model.simulationmodel.SimulationModel;

/**
 * <b>Implementiert:</b> <code>ActionListener</code> <br>
 * <b>In Verbindung stehende Klassen:</b>
 * <code>EditorModel, SimulationModel, TabbedPane, MenuBar</code><br>
 * <br>
 * Listener, der reagiert, wenn der Modus geaendert wird. Falls vom
 * Bearbeitungsmodus in den Simulationsmodus gewechselt wird, werden zuerst die
 * Checks durchgefuehrt und dann das Simulationsmodel aktualisiert (falls die
 * Checks erfolgreich waren), welches die noetigen Daten vom
 * <code>EditorModel</code> erhaelt.
 * 
 * @author Team Locsi
 * 
 */
public class TabListener implements ChangeListener {

	private EditorModel eModel;
	private SimulationModel sModel;
	private TabbedPane tab;
	private MenuBar mBar;
	private ToolBar tools;

	/**
	 * Dieser Konstruktor erzeugt ein neues <code>TabListener</code> - Objekt.
	 * 
	 * @param eModel
	 *            <code>EditorModel</code> - Referenz auf das
	 *            <code>EditorModel</code>
	 * @param sModel
	 *            <code>SimulationModel</code> - Referenz auf das
	 *            <code>SimulationModel</code>
	 * @param tab
	 *            <code>TabbedPane</code> - Referenz auf das
	 *            <code>TabbedPane</code>
	 * @param mBar
	 *            <code>MenuBar</code> - Referenz auf die <code>MenuBar</code>
	 */
	public TabListener(EditorModel eModel, SimulationModel sModel,
			TabbedPane tab, MenuBar mBar, ToolBar tools) {
		this.eModel = eModel;
		this.sModel = sModel;
		this.mBar = mBar;
		this.tab = tab;
		this.tools = tools;
		eModel.getChecker().addObserver(new CheckObserver(this));

	}

	/**
	 * In dieser Methode wird der Wechsel zwischen Bearbeitungs- und
	 * Simulationsmodus ermoeglicht. Falls vom Bearbeitungs- in den
	 * Simulationsmodus gewechselt wird, werden zuerst die Checks mit Hilfe des
	 * <code>Check</code>-Objekts durchgefuehrt. Anschliessend wird das
	 * <code>SimulationModel</code> aktualisiert. Beim Wechsel vom Simulations-
	 * in den Bearbeitungsmodus wird eine eventuell laufende Simulation
	 * angehalten.
	 */
	@Override
	public void stateChanged(ChangeEvent e) {

		// if alterModusVonTabbedPane == 0 <-- bearbeitungsmodus
		if (((TabbedPane) (e.getSource())).getSelectedIndex() == 1) {
			if (eModel.isEmptyCircuit()) {
				this.tab.getIPanel()
						.getInfoArea()
						.addInfoText(
								"Wechseln in den Simulationsmodus bei leerer "
										+ "Schaltung nicht möglich");
				changeBack();
				return;
			}
			
			this.tools.setCurrentMode(ButtonValues.SELECTION_MODE);
			this.mBar.setSimulationModus();
			tab.getEditorToolPanel().setEnabled(false);
			tab.getSimToolPanel().setEnabled(true);
			UtilityDialogs.showCheckDialog(e, eModel);
			eModel.getChecker().startCheckThread();
			
			//es wird mit dem wechseln gewartet, bis der checkthread fertig ist
			try {
				synchronized(eModel) {
					eModel.wait();
				}
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
		} else {
			this.mBar.setEditorModus();
			tab.getEditorToolPanel().setEnabled(true);
			tab.getSimToolPanel().setEnabled(false);
			// Bei Wechsel von Sim.modus nach Bearb.modus die Simulation
			// anhalten, falls sie noch laeuft.
			if (!(sModel.getCHandler().isInterrupted())) {
				sModel.stopSimulation();
			}
		}
	}

	/**
	 * Diese Methode wechselt vom SimulationsModus in den Bearbeitungsmodus
	 */
	protected void changeBack() {
		tab.setSelectedIndex(0);
	}

	/**
	 * Diese Methode veranlasst, dass sich das <code>SimualtionModel</code>
	 * aktualisiert
	 */
	protected void updateSModel() {
		sModel.updateSimulationElements();
	}

	/**
	 * Gibt das <code>TabbedPane</code> zurück
	 * 
	 * @return <code>TabbedPane</code>
	 */
	protected TabbedPane getTab() {
		return tab;
	}

	/**
	 * Diese Klasse stellt einen PseudoObserver dar, der beim Check-Objekt des
	 * EditorModels angemeldet wird, je nach Art der Nachricht, die in der
	 * Update()-Methode uebergeben wird, werden bestimmte Operationen
	 * ausgefuehrt.
	 * 
	 * @author Team Locsi
	 * 
	 */
	public class CheckObserver implements Observer {

		private TabListener tabLi;

		public CheckObserver(TabListener tabLi) {
			this.tabLi = tabLi;
		}

		@Override
		public void update(Observable arg0, Object arg1) {
			if (arg1 instanceof ErrorMessage) {
				ErrorType eType = ((ErrorMessage) arg1).getErrorType();

				switch (eType) {
				case ONE_CHECK_FAILED:

					// falls ein Check fehlschlaegt wird in den
					// BearbeitungsModus zurueck gewechselt
					this.tabLi.changeBack();
					break;

				case ALL_CHECKS_OK:
					
					// falls man sich gerade im SimulationModus befindet wird
					// das sModel upgedated
					if (this.tabLi.getTab().getSelectedIndex() == 1) {
						this.tabLi.updateSModel();
						tab.revalidate();
					}
				default:
					break;
				}
			}
		}
	}
}